#include "sys_linux.h"

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <syslog.h>
#include <signal.h>
//#include <error.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>

static void child_handler(int signum)
{
    switch(signum) {
    case SIGALRM: exit(EXIT_FAILURE); break;
    case SIGUSR1: exit(EXIT_SUCCESS); break;
    case SIGCHLD: exit(EXIT_FAILURE); break;
    }
}

void daemonize(const char* lockfile, const char* user_run)
{
    pid_t pid;
    pid_t sid;
    pid_t parent;
    int lfp = -1;

    /* already a daemon */
    if (getppid() == 1)
        return;



    /* Create the lock file as the current user */
    if (lockfile && lockfile[0]) {
        lfp = open(lockfile, O_RDWR|O_CREAT, 0640);
        if (lfp < 0) {
            syslog(LOG_ERR, "unable to create lock file %s, code=%d (%s)", lockfile, errno, strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    /* Drop user if there is one, and we were run as root */
    if (user_run && user_run[0]) {
        if (getuid() == 0 || geteuid() == 0) {
            struct passwd *pw = getpwnam(user_run);
            if (pw) {
                syslog(LOG_NOTICE, "setting user to %s", user_run);
                setuid(pw->pw_uid);
            }
        }
    }
    /* Trap signals that we expect to recieve */
    signal(SIGCHLD,child_handler);
    signal(SIGUSR1,child_handler);
    signal(SIGALRM,child_handler);


    /* Fork off the parent process */
    pid = fork();
    if (pid < 0) {
        syslog(LOG_ERR, "unable to for daemon, code=%d (%s)", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    if (pid > 0) {
        /* Wait for confirmation from the child via SIGTERM or SIGCHLD, ot
           for two seconds to elapse (SIGALRM). pause should not return. */
        alarm(2);
        pause();

        exit(EXIT_FAILURE);
    }

    /* At this point we are executing as the child process */
    parent = getppid();

    /* Cancel certain signals */
    signal(SIGCHLD, SIG_DFL);  /* A child process dies */
    signal(SIGTSTP, SIG_IGN);  /* Various TTY signals */
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGHUP,  SIG_IGN);  /* Ignore hangup signal */
    signal(SIGTERM, SIG_DFL);  /* Die on SIGTERM */

    /* Change the file mode mask */
    umask(0);

    /* Create a new SID for the child process */
    sid = setsid();
    if (sid < 0) {
        syslog(LOG_ERR, "unable to change directory to %s, code %d (%s)", "/", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    /* Change the current working directory */
    if ((chdir("/")) < 0) {
        exit(EXIT_FAILURE);
    }

    /* Redirect standard files to /dev/null */
    freopen("/dev/null", "r", stdin);
    freopen("/dev/null", "r", stdout);
    freopen("/dev/null", "r", stderr);

    /* Tell the parent process that we are A-okey */
    kill(parent, SIGUSR1);
}
